package com.cn.dao;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.Date;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.springframework.jdbc.InvalidResultSetAccessException;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;

import com.cn.bean.SimpleList;
import com.cn.dao.model.DataBlock;
import com.cn.dao.model.NamedDataBlock;
import com.cn.xc.utils.BeanUtil;
import com.cn.xc.utils.DateUtil;
import com.cn.xc.utils.ObjectUtil;
import com.cn.xc.utils.StringUtil;

/**
 * FUN: 记录结果集
 *
 * @author sushy
 * @date 2016-3-23 
 */
public class RowSet implements IJdbc{
	
	private SqlRowSet rs = null;
	private SqlRowSetMetaData rm = null;
	private String[] propertyNames = null;

	RowSet() {
		this(null);
	}

	public RowSet(SqlRowSet rs) {
		this.rs = rs;
		if (rs != null) {
			this.rm = rs.getMetaData();
		}
	}

	public int findColumn(String s) {
		if (rs != null) {
			try {
				return rs.findColumn(s);
			} catch (InvalidResultSetAccessException e) {
				logger.error("列名无效:" + s);
			}
		}
		return -1;
	}

	/**
	 * 读取下一条记录，如果没有就返回false
	 * 
	 * @return
	 */
	public boolean next() {
		if (rs == null) {
			return false;
		}
		try {
			return rs.next();
		} catch (InvalidResultSetAccessException e) {
			e.printStackTrace();
			logger.error("游标错误！", e);
			return false;
		}
	}

	/**
	 * 是否空记录集
	 * 
	 * @return
	 */
	public boolean isEmpty() {
		if (rs == null) {
			return true;
		}
		if (rs.isBeforeFirst()) {
			return false;
		}
		return true;
	}

	/**
	 * 返回列数
	 * 
	 * @return
	 */
	public int getCol() {
		if (rs == null || rm == null) {
			return 0;
		}
		return rm.getColumnCount();
	}

	/**
	 * 返回字段名
	 * 
	 * @return
	 */
	public String[] getColumnNames() {
		if (rs == null || rm == null) {
			return null;
		}
		return rm.getColumnNames();
	}

	public String[] getPropertyNames() {
		if (propertyNames != null) {
			return propertyNames;
		}

		String[] columnNames = getColumnNames();
		if (columnNames != null && columnNames.length > 0) {
			propertyNames = new String[columnNames.length];
			for (int i = 0; i < columnNames.length; ++i) {
				propertyNames[i] = BaseMapper.genPropertiesName(columnNames[i]);
			}
		}
		return propertyNames;
	}

	/**
	 * 获取i列值
	 * 
	 * @param index
	 * @return
	 */
	public long getLong(int index) {
		Object obj = this.getObject(index);
		if (obj == null) {
			return 0;// null默认返回0
		}

		// 数值类型转换
		if (obj instanceof Number) {
			return ((Number) obj).longValue();
		}

		// 尝试强转
		return Long.valueOf(obj.toString());
	}

	/**
	 * 获取colName字段值
	 * 
	 * @param colName
	 * @return
	 */
	public long getLong(String colName) {
		Object obj = this.getObject(colName);
		return ObjectUtil.toLong(obj);
	}

	/**
	 * 获取i列值
	 * 
	 * @param index
	 * @return
	 */
	public double getDouble(int index) {
		Object obj = this.getObject(index);
		return ObjectUtil.toDouble(obj);
	}

	/**
	 * 获取colName字段值
	 * 
	 * @param colName
	 * @return
	 */
	public double getDouble(String colName) {
		Object obj = this.getObject(colName);
		return ObjectUtil.toDouble(obj);
	}

	/**
	 * 获取i列值
	 * 
	 * @param index
	 * @return
	 */
	public String getString(int index) {
		Object obj = this.getObject(index);
		return ObjectUtil.toString(obj);
	}

	/**
	 * 获取i列值
	 * 
	 * @param colName
	 * @return
	 */
	public String getString(String colName) {
		Object obj = this.getObject(colName);
		return ObjectUtil.toString(obj);
	}

	/**
	 * 获取日期值
	 * 
	 * @param index
	 * @return
	 */
	public Date getDate(int index) {
		Object obj = this.getObject(index);
		return ObjectUtil.toDate(obj);
	}

	/**
	 * 获取colName字段值
	 * 
	 * @param colName
	 * @return
	 */
	public Date getDate(String colName) {
		Object obj = this.getObject(colName);
		return ObjectUtil.toDate(obj);
	}

	/**
	 * 获取字段值
	 * 
	 * @param index
	 * @return
	 */
	public Object getObject(int index) {
		if (rs == null || rm == null) {
			return null;
		}

		Object obj = rs.getObject(index);
		if (obj == null) {
			return null;
		}

		// Clob类型
		if (obj instanceof Clob) {
			Clob cb = (Clob) obj;
			try {
				obj = cb.getSubString(1, (int) cb.length());
			} catch (SQLException e) {
				obj = rs.getString(index);
			}
		}

		// 日期类型
		else if (obj.getClass().getName().startsWith("oracle.sql.TIMESTAMP")) {
			try {
				Class<?> clz = obj.getClass();
				Method m = clz.getMethod("timestampValue");
				obj = (java.sql.Timestamp) m.invoke(obj);
			} catch (Throwable t) {
				t.printStackTrace();
				logger.error("获取字段值异常", t);
			}
		} else if (obj.getClass().getName().startsWith("oracle.sql.DATE")) {
			String metaDataClassName = rm.getColumnClassName(index);
			if ("java.sql.Timestamp".equals(metaDataClassName)
					|| "oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
				obj = rs.getTimestamp(index);
			} else {
				obj = rs.getDate(index);
			}
		} else if (obj instanceof Date
				&& "java.sql.Timestamp".equals(rm.getColumnClassName(index))) {
			obj = rs.getTimestamp(index);
		}

		// 数值类型或字符串类型
		return obj;
	}

	/**
	 * 获取字段值
	 * 
	 * @param colName
	 * @return
	 */
	public Object getObject(String colName) {
		if (rs == null || rm == null) {
			return null;
		}
		int index = findColumn(colName);
		if (index < 0) {
			return null;
		}
		return this.getObject(index);
	}

	/**
	 * XML
	 * 
	 * @param encoding
	 * @return
	 */
	public String toXml() {
		return toXml("UTF-8");
	}

	/**
	 * 将当前记录保存到bean对象(通过返回字段驱动)
	 */
	public void saveToBean(Object bean) {
		if (bean == null) {
			return;
		}

		String[] propertyNames = getPropertyNames();
		if (propertyNames == null || propertyNames.length <= 0) {
			return;
		}

		for (int i = 0; i < propertyNames.length; ++i) {
			PropertyDescriptor pd = BeanUtil.getPropertyDescriptor(
					bean.getClass(), propertyNames[i]);
			if (pd == null) {
				continue;
			}

			Object value = null;
			if (pd.getPropertyType() == long.class
					|| pd.getPropertyType() == Long.class) {
				value = this.getLong(i + 1);
			} else if (Date.class.isAssignableFrom(pd.getPropertyType())) {
				value = this.getDate(i + 1);
			} else if (pd.getPropertyType() == double.class
					|| pd.getPropertyType() == Double.class) {
				value = this.getDouble(i + 1);
			} else {
				value = this.getString(i + 1);
			}
			if (value == null) {
				continue;
			}
			BeanUtil.setPropertyValue(bean, pd, value);
		}
	}

	/**
	 * 输出XML
	 * 
	 * @return
	 */
	public String toXml(String encoding) {
		StringWriter sw = null;
		try {
			sw = new StringWriter();
			XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance()
					.createXMLStreamWriter(sw);
			xmlWriter.writeStartDocument(encoding, null);
			xmlWriter.writeStartElement("Data");

			String colNames[] = getColumnNames();
			String colText = null;
			int col = this.getCol();
			while (this.next()) {
				xmlWriter.writeStartElement("Row");
				for (int j = 0; j < col; ++j) {
					xmlWriter.writeStartElement(colNames[j]);
					Object obj = this.getObject(j + 1);
					if (obj == null) {
						colText = null;
					} else if (obj instanceof Number) {
						colText = ((Number) obj).toString();
					} else if (obj instanceof Date) {
						colText = DateUtil.date2Str((Date) obj);
					} else {
						colText = obj.toString();
					}

					if (StringUtil.isNotEmpty(colText)) {
						if (colText.length() > 1000) {
							xmlWriter.writeCData(colText);
						} else {
							xmlWriter.writeCharacters(colText);
						}
					}
					xmlWriter.writeEndElement();// Col
				}
				xmlWriter.writeEndElement();// Row
			}
			xmlWriter.writeEndElement();// Data
			xmlWriter.writeEndDocument();
			return sw.toString();
		} catch (XMLStreamException e) {
			logger.error("XML报文流异常", e);
			e.printStackTrace();
			return null;
		} finally {
			if (sw != null) {
				try {
					sw.close();
					sw = null;
				} catch (IOException e) {
				}
			}
		}
	}

	/**
	 * 输出到DataBlock
	 * 
	 * @param rs
	 * @return
	 */
	public DataBlock toDataBlock() {
		DataBlock dblk = new DataBlock();
		int i = 0;
		int col = this.getCol();
		while (this.next()) {
			for (int j = 0; j < col; ++j) {
				Object obj = this.getObject(j + 1);
				if (obj == null) {
					dblk.add(i, j, (String) null);
				} else if (obj instanceof Number) {
					dblk.add(i, j, ((Number) obj).longValue());
				} else if (obj instanceof Date) {
					dblk.add(i, j, (Date) obj);
				} else {
					dblk.add(i, j, obj.toString());
				}
			}
			++i;
		}
		return dblk;
	}

	/**
	 * 输出到DataBlock
	 * 
	 * @param rs
	 * @return
	 */
	public NamedDataBlock toNamedDataBlock() {
		return toNamedDataBlock(false);
	}

	/**
	 * 输出到DataBlock
	 * 
	 * @param rs
	 * @return
	 */
	public NamedDataBlock toNamedDataBlock(boolean isLowerCase) {
		NamedDataBlock dblk = new NamedDataBlock();
		int i = 0;
		String colNames[] = getColumnNames();
		int col = this.getCol();
		if (isLowerCase) {
			for (i = 0; i < col; ++i) {
				colNames[i] = colNames[i].toLowerCase();
			}
		}

		i = 0;
		while (this.next()) {
			for (int j = 0; j < col; ++j) {
				Object obj = this.getObject(j + 1);
				if (obj == null) {
					dblk.add(i, colNames[j], (String) null);
				} else if (obj instanceof Number) {
					dblk.add(i, colNames[j], ((Number) obj).longValue());
				} else if (obj instanceof Date) {
					dblk.add(i, colNames[j], (Date) obj);
				} else {
					dblk.add(i, colNames[j], obj.toString());
				}
			}
			++i;
		}
		return dblk;
	}

	/**
	 * 获取查询字段
	 * 
	 * @return
	 */
	public static String getSelectField(Class<?> beanCls, String alias,
			String prefix) {
		PropertyDescriptor[] pds = BeanUtil.getPropertyDescriptors(beanCls);
		if (pds == null || pds.length <= 0) {
			return null;
		}

		StringBuilder sb = new StringBuilder();
		Class<?> cls = null;
		String colName = null;
		String propertyName = null;
		boolean isHaveAlias = StringUtil.isNotEmpty(alias);
		boolean isHavePrefix = StringUtil.isNotEmpty(prefix);
		int i = 0;
		for (PropertyDescriptor pd : pds) {
			cls = pd.getPropertyType();
			if (cls == Class.class || SimpleList.class.isAssignableFrom(cls)) {
				continue;
			}

			propertyName = pd.getName();
			colName = StringUtil.parsePropertyName2ColumnName(propertyName);

			if (i > 0) {
				sb.append(",");
			}
			if (isHaveAlias) {
				sb.append(alias);
				sb.append(".");
			}
			sb.append(colName);
			sb.append(" as ");
			if (isHavePrefix) {
				sb.append(prefix);
			}
			sb.append(propertyName);
			++i;
		}

		return sb.toString();
	}

	/**
	 * 读取rs到bean
	 * 
	 * @param resultset
	 * @param objBean
	 * @return
	 */
	public void saveToBean(Object bean, String prefix, boolean isParse) {
		PropertyDescriptor[] pds = BeanUtil.getPropertyDescriptors(bean
				.getClass());
		if (pds == null || pds.length <= 0) {
			return;
		}

		int colNum = getCol();
		int colIndex = -1;
		String colName = null;
		boolean isHavePrefix = StringUtil.isNotEmpty(prefix);
		Class<?> cls = null;
		for (PropertyDescriptor pd : pds) {
			cls = pd.getPropertyType();
			if (cls == Class.class || SimpleList.class.isAssignableFrom(cls)) {
				continue;
			}

			colName = pd.getName();
			if (isParse) {
				colName = StringUtil.parsePropertyName2ColumnName(colName);
			}

			if (isHavePrefix) {
				colName = prefix + colName;
			}

			colIndex = findColumn(colName);
			if (colIndex < 1 || colIndex > colNum) {
				continue;// 没有这列
			}

			if (cls == Long.class || cls == long.class) {// long类型
				BeanUtil.setPropertyValue(bean, pd, getLong(colIndex));
			} else if (cls == String.class) {// String类型
				BeanUtil.setPropertyValue(bean, pd, getString(colIndex));
			} else if (Date.class.isAssignableFrom(cls)) {// 日期类型
				BeanUtil.setPropertyValue(bean, pd, getDate(colIndex));
			}
		}
	}

}
